DEFINITION MODULE File;

   (* This module defines data types and procedures for performing *)
   (* DIRECT ACCESS file I/O with a user defined logical record.   *)


   FROM SYSTEM IMPORT ADDRESS;
   IMPORT GEMDOS;


   CONST
      EOK           = GEMDOS.EOK;
      UnknownError  = GEMDOS.Error;
      InvalidHandle = GEMDOS.EIHndl;
      BadRequest    = GEMDOS.EBadRq;
      SeekError     = GEMDOS.ESeek;
      ReadFailure   = GEMDOS.EReadF;
      WriteFailure  = GEMDOS.EWritF;
      EndOfFile     = -1000;


   TYPE RecordIdType = LONGCARD;

      (* Defines a type for uniquely identifying a single record. *)

   TYPE PossibleFields = (NextKey, PrevKey, NextRecord, PrevRecord);

   TYPE RecordHeaderType = RECORD
      NextKeyId    : RecordIdType; (* Id of previous record with different primary key *)
      PrevKeyId    : RecordIdType; (* Id of next record with different primary key *)
      NextRecordId : RecordIdType; (* Id of next logical record *)
      PrevRecordId : RecordIdType; (* Id of previous logical record *)
   END;

      (* This declaration, defining information required by the File   *)
      (* module, is provided for inclusion in the user defined record  *)
      (* as the first component.  Immediately following this type, the *)
      (* user must declare two additional fields - a key and a subkey. *)
      (* Each field should be declared as ARRAY [0..n] OF CHAR, where  *)
      (* n is the length of the key (or subkey).  An example of a      *)
      (* complete user defined record is :                             *)
      (*                                                               *)
      (*    TYPE UserDefinedRecord = RECORD                            *)
      (*       RecordHeader : File.RecordHeaderType;                   *)
      (*       Key          : ARRAY [0..3] OF CHAR;                    *)
      (*       SubKey       : ARRAY [0..5] OF CHAR;                    *)
      (*       UserData     : SomeUserDefinedType;                     *)
      (*    END;                                                       *)
 
   TYPE GetKeyProc = PROCEDURE ( ADDRESS, VAR ARRAY OF CHAR );

      (* Defines a type for retrieving the key from the user record. *)

   TYPE GetSubKeyProc = PROCEDURE ( ADDRESS, VAR ARRAY OF CHAR );

      (* Defines a type for retrieving the subkey from the user record. *)

   TYPE FileIdType = RECORD
      Valid         : BOOLEAN;       (* True if valid file handle *)
      FileHandle    : INTEGER;       (* GEMDOS file handle *)
      FileSize      : LONGCARD;      (* "High Water" mark of file size *)
      FilePosition  : RecordIdType;  (* Id of current record *)
      RecordSize    : CARDINAL;      (* Size of user defined logical record *)
      NumberRecords : LONGCARD;      (* Number of logical records in file *)
      GetKey        : GetKeyProc;
      GetSubKey     : GetSubKeyProc;
      FreeListId    : RecordIdType;  (* First free record id *)
      FirstRecordId : RecordIdType;  (* First data record id *)
      NextRecordId  : RecordIdType;  (* Id of next record relative to current *)
      PrevRecordId  : RecordIdType;  (* Id of previous record relative to current *)
   END;

      (* Defines a type for uniquely identifying a single file.  Note *)
      (* that this type is private to the File module, and as such    *)
      (* must not be modified by other modules.  However, information *)
      (* may be read from the record without adverse side effects.    *)


   VAR Status : INTEGER;

      (* Contains the status of the last File operation. The possible *)
      (* values are documented in module GEMDOS.                      *)


   PROCEDURE DosError ( ErrorCode : INTEGER );

      (* Display the Dos Error in an alert box. *)


   PROCEDURE GetField (
      Field      : (* IN *) PossibleFields;
      UserRecord : (* IN *) ADDRESS ) : RecordIdType;

      (* Retrieves the information from the record header *)
      (* field specified by Field.                        *)


   PROCEDURE Create (
      VAR FileName   : (* IN *)  ARRAY OF CHAR;
      RecordSize     : (* IN *)  CARDINAL;
      GetKey         : (* IN *)  GetKeyProc;
      GetSubKey      : (* IN *)  GetSubKeyProc;
      VAR FileId     : (* OUT *) FileIdType );

      (* Creates a zero length disk file with name FileName.      *)
      (* RecordSize contains the total size of the user defined   *)
      (* record in bytes, and must be larger than the sum of the  *)
      (* sizes of the record header, key, and subkey.  If the     *)
      (* routine was successful, FileId contains a value uniquely *)
      (* identifying the file for further access.                 *)


   PROCEDURE Open (
      VAR FileName   : (* IN *)  ARRAY OF CHAR;
      GetKey         : (* IN *)  GetKeyProc;
      GetSubKey      : (* IN *)  GetSubKeyProc;
      VAR FileId     : (* OUT *) FileIdType );

      (* Opens the existing file named FileName.  Note that since  *)
      (* the file attributes are stored in the file (record size,  *)
      (* key size, subkey size, etc), this information does not    *)
      (* need to be specified by the user when invoking this       *)
      (* routine.  If the routine was successful, FileId contains  *)
      (* a value uniquely identifying the file for further access. *)


   PROCEDURE Read (   
      VAR FileId   : (* IN *)  FileIdType;  
      Data         : (* OUT *) ADDRESS;
      VAR RecordId : (* OUT *) RecordIdType );

      (* Reads the record located at the current file position     *)
      (* into Data.  If the routine is successful, the record id   *)
      (* is placed in RecordId.  Otherwise, the contents of        *)
      (* RecordId is undefined.  Upon completion of this routine,  *)
      (* the current file position is advanced to the next logical *)
      (* record.                                                   *)


   PROCEDURE Write (
      VAR FileId   : (* IN *)  FileIdType;  
      Data         : (* IN *)  ADDRESS;
      VAR RecordId : (* OUT *) RecordIdType );

      (* Writes the contents of Data into the record located at the    *)
      (* current file position.  If the routine is successful, the     *)
      (* record id is placed in RecordId.  Otherwise, the contents of  *)
      (* RecordId is undefined.  Upon completions of this routine, the *)
      (* current file position is advanced to the next logical record. *)
      (* Note that the contents of the key and subkey fields must not  *)
      (* be changed since the record was last read.  In order to       *)
      (* modify the contents of the key or subkey, delete the record   *)
      (* and then reinsert it.  This will reinsert the record in the   *)
      (* proper place in the file.                                     *)
        

   PROCEDURE InsertRecord (
      VAR FileId   : (* IN *)  FileIdType;  
      VAR Key      : (* IN *)  ARRAY OF CHAR;
      VAR SubKey   : (* IN *)  ARRAY OF CHAR;
      Data         : (* IN *)  ADDRESS;
      WorkBuffer   : (* IN *)  ADDRESS;
      VAR RecordId : (* OUT *) RecordIdType );

      (* Inserts the contents of Data into the file associated with    *)
      (* FileId.  The record is inserted into the file in ascending    *)
      (* ASCII order (based on the contents of the key and subkey).    *)
      (* If the routine is successful, RecordId contains a value       *)
      (* uniquely identifying the record.  Otherwise, the contents     *)
      (* of RecordId is undefined.  Note that the parameter            *)
      (* WorkBuffer, used internally by the routine for temporary      *)
      (* storage, must be of the same type as the user defined record. *)


   PROCEDURE DeleteRecord (   
      VAR FileId : (* IN *) FileIdType;  
      RecordId   : (* IN *) RecordIdType;
      WorkBuffer : (* IN *) ADDRESS );

      (* Deletes the record identified by RecordId from the file      *)
      (* associated with FileId.  Note that the parameter WorkBuffer, *)
      (* used internally by the routine for temporary storage, must   *)
      (* be of the same type as the user defined record.              *)


   PROCEDURE Retreat (
      VAR FileId   : (* IN *)  FileIdType;  
      Data         : (* OUT *) ADDRESS;
      VAR RecordId : (* OUT *) RecordIdType );

      (* Moves the current file position to the previous logical    *)
      (* record and then places the contents of the record in Data. *)
      (* If the routine is successful, the id of the record is      *)
      (* placed in RecordId.  Otherwise, the contents of RecordId   *)
      (* is undefined.  Note that the parameter WorkBuffer, used    *)
      (* internally by the routine for temporary storage, must be   *)
      (* of the same type as the user defined record.               *)


   PROCEDURE Seek (
      VAR FileId : (* IN *)  FileIdType;
      RecordId   : (* IN *)  RecordIdType;
      Data       : (* OUT *) ADDRESS );

      (* Moves the current file position to the record identified by   *)
      (* RecordId, and then places the contents of the record in Data. *)


   PROCEDURE Find (
      VAR FileId      : (* IN *)  FileIdType;
      VAR Key         : (* IN *)  ARRAY OF CHAR;
      VAR SubKey      : (* IN *)  ARRAY OF CHAR;  
      Data            : (* OUT *) ADDRESS;
      VAR FoundKey    : (* OUT *) BOOLEAN;
      VAR KeyId       : (* OUT *) RecordIdType;
      VAR FoundRecord : (* OUT *) BOOLEAN;
      VAR RecordId    : (* OUT *) RecordIdType );

      (* Searches the file associated with FileId for a record      *)
      (* containing the contents of Key and Subkey.  Note that      *)
      (* either of the keys may be a null string - matching any     *)
      (* record.  If a record was found, the contents of the record *)
      (* is placed in Data, and RecordId is set to a value uniquely *)
      (* identifying the record.  The current file position is then *)
      (* set to the start of the next record.  Otherwise, if the    *)
      (* keys could not be found, the contents of RecordId are      *)
      (* undefined, with KeyId and RecordId pointing to the last    *)
      (* key and record read during the search, respectively.       *)


   PROCEDURE Close ( VAR FileId : (* IN *) FileIdType );

      (* Closes the file associated with FileId. *)


   PROCEDURE Copy;

      (* Copy one file to another. *)


   PROCEDURE Delete;

      (* Delete a file. *)


END File.

